package handler

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"
	"math"

	"kop/framework/broker"
	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/libs/redis"
	configuration2 "kop/modules/captain/configuration"
	"kop/modules/captain/configuration/fetter"
	entity2 "kop/modules/captain/entity"
	"kop/modules/item/consts"
	"kop/modules/maid/configuration"
	"kop/modules/maid/entity"
	"kop/modules/maid/models"
	"kop/modules/role/configuration/level"
	"kop/pb"
	"kop/pb/service"
	"kop/util/cd"
	"kop/util/rand"

	achievementC "kop/modules/achievement/client"
	activityC "kop/modules/activity/client"
	awardM "kop/modules/award/models"
	buildingC "kop/modules/building/client"
	captain2 "kop/modules/captain/models"
	itemC "kop/modules/item/client"
	mailC "kop/modules/mail/client"
	questC "kop/modules/quest/client"
	roleC "kop/modules/role/client"
)

type MaidServiceHandler struct {
	context2.Context
}

func (h *MaidServiceHandler) GetMaids(_ context.Context, req *service.RoleRequest, resp *service.Maids) error {

	resp.Maids = models.GetMaids(database.DB, req.RoleID)

	var data = models.GetLostMaids(req.RoleID)

	for _, maid := range resp.Maids {
		if v, ok := data[maid.MaidID]; ok {
			maid.Sec = v.GetSec()
		}
	}

	return nil
}

func (h *MaidServiceHandler) FriendMaids(_ context.Context, req *service.RoleRequest, resp *service.Maids) error {

	resp.Maids = models.FriendMaids(database.DB, req.RoleID)

	var data = models.GetLostMaids(req.RoleID)

	for _, maid := range resp.Maids {
		if v, ok := data[maid.MaidID]; ok {
			maid.Sec = v.GetSec()
		}
	}

	return nil
}

func (h *MaidServiceHandler) GetMaid(_ context.Context, req *service.DefaultRequest, resp *pb.Maid) error {

	if _, err := configuration.GetByMaidID(req.Value); err != nil {
		return err
	}

	*resp = models.GetMaid(database.DB, req.RoleID, req.Value)
	return nil
}

func (h *MaidServiceHandler) Create(_ context.Context, req *service.DefaultRequest, maid *pb.Maid) error {

	var roleID = req.RoleID

	*maid = models.Create(roleID, req.Value)

	var role = roleC.GetRole(roleID)
	// 结交侍女：提督[提督名称七个字]魅力超群，结交了[侍女名称] ！
	_ = broker.ServerPublish(role.ServerID, pb.WsCode_CodeMessage, &pb.ServerMessage{Type: 4, Name: []string{role.Name}, Nums: []int32{maid.MaidID}})
	go achievementC.MaidNum(roleID)

	var user = context2.NewUser(roleID, role.ServerID)
	go h.更新对应羁绊的船长(context2.WithUser(context2.NewContext(user), user), maid.MaidID)
	return nil
}

func (h *MaidServiceHandler) Present(ctx context.Context, req *pb.APIMaidPresentRequest, resp *pb.Maid) error {

	var roleID = h.GetRoleID(ctx)

	const (
		Present1 int32 = 101021 // 4种礼物
		Present2 int32 = 101022
		Present3 int32 = 101023
		Present4 int32 = 101024
	)

	var intimacy, charm int32

	switch req.ItemID {
	case Present1:
		intimacy = 1
	case Present2:
		intimacy = 2
	case Present3:
		charm = 1
	case Present4:
		charm = 2
	default:
		return errors.ParamError
	}

	*resp = models.GetMaid(database.DB, roleID, req.MaidID)

	resp.Intimacy += intimacy
	resp.Charm += charm
	models.Save(database.DB, resp)

	if intimacy > 0 {
		activityC.AddPoints(roleID, h.GetServerID(ctx), activityC.Intimacy, int64(intimacy))
	}

	return nil
}

func (h *MaidServiceHandler) GetMaidDate(_ context.Context, req *service.RoleRequest, resp *pb.MaidDate) error {

	var vip = roleC.GetVIP(req.RoleID)
	*resp = *buildPbMaidDate(models.GetMaidDate(database.DB, req.RoleID, vip), vip)

	return nil
}

func (h *MaidServiceHandler) SaveDate(_ context.Context, req *pb.MaidDate, resp *pb.MaidDate) error {

	var vip = roleC.GetVIP(req.RoleID)
	var data = models.GetMaidDate(database.DB, req.RoleID, vip)
	data.DateNum = req.DateNum
	data.BarLuck = req.BarLuck.Luck
	data.BarWishNum = req.BarLuck.WishNum
	data.DateTime = cd.NewCD(models.DateCD, vip.Date).SetTime(data.DateTime).SetNum(req.BarDateNum)
	data.DrinkTime = cd.NewCD(models.DrinkCD, vip.Drink).SetTime(data.DrinkTime).SetNum(req.BarDrinkNum)

	models.SaveMaidDate(database.DB, &data)

	*resp = *buildPbMaidDate(data, vip)
	return nil
}

func (h *MaidServiceHandler) Drink(ctx context.Context, _ *empty.Empty, resp *service.MaidDrinkResp) (err error) {

	var roleID = h.GetRoleID(ctx)
	var vip = roleC.GetVIP(roleID)
	var data = models.GetMaidDate(database.DB, roleID, vip)

	var n, _ = cd.NewCD(models.DrinkCD, vip.Drink).SetTime(data.DrinkTime).GetNum()

	if n <= 0 {
		return errors.ParamError
	}

	// 是否结交侍女
	var isMaid = rand.IsWin(0.5 * float32(data.BarLuck) / 100)

	data.DrinkTime = cd.NewCD(models.DrinkCD, vip.Drink).SetTime(data.DrinkTime).SetNum(n - 1)
	data.BarLuck -= 2
	if data.BarLuck < 0 {
		data.BarLuck = 0
	}

	models.SaveMaidDate(database.DB, &data)
	defer func() {
		if err == nil {
			resp.MaidDate = buildPbMaidDate(data, vip)
		}
	}()

	if isMaid {

		var noFriendConfigs []rand.Value
		var friendConfigs []rand.Value

		var maids = models.FriendMaids(database.DB, roleID)
		var maidMap = make(map[int32]*pb.Maid, len(maids))
		for _, maid := range maids {
			maidMap[maid.MaidID] = maid
		}

		var allFriend = true

		var maid *pb.Maid

		for _, conf := range configuration.BarEventConfigs {
			if conf.Type == configuration.MaidNotFriend {

				if v, ok := maidMap[conf.MaidID]; !ok || !v.IsFriend {
					// 找到未结交的侍女
					allFriend = false
					noFriendConfigs = append(noFriendConfigs, conf)
				}
			} else if conf.Type == configuration.MaidFriend {

				if v, ok := maidMap[conf.MaidID]; ok && v.IsFriend {
					friendConfigs = append(friendConfigs, conf)
				}
			}
		}

		if !allFriend && rand.IsWin(0.5) {
			// 一半的概率未结交 友好度加1
			var eventConf = rand.WithWeight(noFriendConfigs, 1)[0].(configuration.BarEventConf)

			var res = models.GetMaid(database.DB, roleID, eventConf.MaidID)
			maid = &res

		} else {
			// 一半的概率已结交 亲密+1
			var conf = rand.WithWeight(friendConfigs, 1)[0].(configuration.BarEventConf)

			for _, v := range maids {
				if v.MaidID == conf.MaidID {
					maid = v
					break
				}
			}

			if maid == nil {
				return errors.New("酒吧事件侍女配置出错")
			}
		}

		maid.Intimacy += 1

		if !maid.IsFriend {
			if conf, err := configuration.GetByMaidID(maid.MaidID); err != nil {
				return err
			} else if maid.Intimacy >= conf.NeedFavour {
				// 友好度满 转正结交
				maid.Intimacy = 0
				maid.IsFriend = true
			}
		}

		models.Save(database.DB, maid)
		models.UpdateIntimacy(database.DB, roleID, h.GetServerID(ctx))

		resp.Maid = maid
		return nil
	} else {

		var allGet = true
		var have []rand.Value   // 已获得的
		var noHave []rand.Value // 未获得的

		var captainMap = make(map[int32]*entity2.Captain)
		for _, v := range captain2.New(roleID).Captains {
			captainMap[v.CaptainID] = v
		}

		for _, conf := range configuration.BarEventConfigs {
			if conf.Type == configuration.Captain {

				if _, ok := captainMap[conf.CaptainID]; !ok {
					allGet = false
					noHave = append(noHave, rand.Struct{ID: conf.CaptainID, Weight: conf.Weight})
				} else {
					have = append(have, rand.Struct{ID: conf.CaptainID, Weight: conf.Weight})
				}
			}
		}

		if len(have) == 0 || !allGet && rand.IsWin(0.5) {
			// 偶遇未招募船长
			var conf = rand.WithWeight(noHave, 1)[0]
			resp.CaptainID = conf.GetID()

		} else {

			var conf = rand.WithWeight(have, 1)[0]

			resp.CaptainID = conf.GetID()
			resp.Luck = true
		}

		return nil
	}
}

func (h *MaidServiceHandler) ClassUp(ctx context.Context, req *pb.APIMaidClassUpRequest, resp *pb.APIMaidReply) error {

	var roleID = h.GetRoleID(ctx)

	if _, err := configuration.GetByMaidID(req.MaidID); err != nil {
		return err
	}

	var classConf, err = configuration.GetByMaidClass(req.Class)
	if err != nil {
		return err
	}

	var maid = models.GetMaid(database.DB, roleID, req.MaidID)
	if !maid.IsFriend {
		return errors.New("侍女未结交")
	}

	if maid.Class == req.Class { // 没变化
		return nil
	}

	if maid.Intimacy < classConf.NeedIntimacy {
		return errors.New("亲密度不足")
	}

	if maid.Charm < classConf.NeedCharm {
		return errors.New("侍女魅力不足")
	}

	var n = models.CountByClass(database.DB, roleID, req.Class)
	if n >= classConf.CountMax {
		return errors.New("侍女封位人数已满")
	}

	maid.Class = req.Class
	models.Save(database.DB, &maid)

	go h.更新对应羁绊的船长(ctx, maid.MaidID)

	resp.Maid = &maid
	return nil
}

func (h *MaidServiceHandler) SkillUp(ctx context.Context, req *pb.APIMaidSkillUpRequest, resp *pb.APIMaidReply) error {

	var roleID = h.GetRoleID(ctx)

	if req.SkillIndex < 1 || req.SkillIndex > 7 {
		return errors.ParamError
	}

	if _, err := configuration.GetByMaidID(req.MaidID); err != nil {
		return err
	}

	var maid = models.GetMaid(database.DB, roleID, req.MaidID)
	if !maid.IsFriend {
		return errors.ParamError
	}

	var needExp = maid.Skills[req.SkillIndex-1].ExpCost
	if needExp == 0 { // 已满级
		return errors.ParamError
	}

	if maid.Exp < needExp { // 经验不足
		return errors.ParamError
	}

	var needIntimacy int32
	switch req.SkillIndex {
	case 1:
		needIntimacy = 10
	case 2:
		needIntimacy = 20
	case 3:
		needIntimacy = 50
	case 4:
		needIntimacy = 100
	case 5:
		needIntimacy = 150
	case 6:
		needIntimacy = 200

	case 7:

		for i := 0; i < 6; i++ { // 前6个技能全满级
			if maid.Skills[i].ExpCost != 0 {
				return errors.ParamError
			}
		}

		needIntimacy = 200
	}

	if maid.Intimacy < needIntimacy {
		return errors.ParamError
	}

	maid.Skills[req.SkillIndex-1].Level += 1
	maid.Exp -= needExp
	models.Save(database.DB, &maid)

	go h.更新对应羁绊的船长(ctx, maid.MaidID)

	resp.Maid = &maid
	return nil
}

const (
	//AwardType1    int32 = 1 // 最好回答
	//AwardType2    int32 = 2
	//AwardType3    int32 = 3
	//AwardType4    int32 = 4 // 最差回答
	TypeLuck    int32 = 5 // 神格觉醒
	TypeFail    int32 = 6 // 失败
	TypeBar     int32 = 7 // 酒吧约会
	TypeBarLuck int32 = 8 // 酒吧觉醒
)

func (h *MaidServiceHandler) BarDate(ctx context.Context, _ *empty.Empty, resp *pb.MaidDateResp) error {

	var roleID = h.GetRoleID(ctx)
	//var maidDate = models.GetMaidDate(database.DB, roleID, roleC.GetVIP(roleID))

	//if maidDate.BarDateNum <= 0 {
	//	return errors.ParamError
	//}

	// 随机一个侍女约会
	var maids = models.FriendMaids(database.DB, roleID)
	var maid = maids[rand.Int(0, len(maids)-1)]

	var Type int32 // 奖励类型
	if h.isAwake(maid) {

		resp.Luck = true
		Type = TypeBarLuck
	} else {

		Type = TypeBar
	}

	var configs = configuration.GetByDateAwardType(Type)

	resp.Maid = maid
	resp.Exp = h.exp(maid, configs[0])

	maid.Exp += resp.Exp
	models.Save(database.DB, maid)
	//models.SaveMaidDate(database.DB, &maidDate)

	// quest
	//questC.Date(roleID)
	return nil
}

func (h *MaidServiceHandler) DateStep1(ctx context.Context, req *pb.DateStep1Request, resp *pb.APIMaidDateReply) (err error) {

	var roleID = h.GetRoleID(ctx)
	maidConf, err := configuration.GetByMaidID(req.MaidID)
	if err != nil {
		return err
	}

	conf, err := configuration.GetByDateIDPlaceID(maidConf.DateID, req.PlaceID)
	if err != nil {
		return err
	}

	var vip = roleC.GetVIP(roleID)
	var data = models.GetMaidDate(database.DB, roleID, vip)
	if data.DateNum <= 0 {
		return errors.ParamError
	}

	var maid = models.GetMaid(database.DB, roleID, req.MaidID)

	var Type int32 // 奖励类型

	if len(conf.DateDialogs) > 0 {

		// 是否觉醒 默认15%概率
		if h.isAwake(&maid) {
			resp.Success = true
			resp.Luck = true
			Type = TypeLuck

		} else {
			// step2 先不扣次数
			resp.Success = true
			resp.Luck = false
			return nil
		}

	} else {
		// 约会失败
		resp.Success = false
		Type = TypeFail
	}

	// 如果
	data.DateNum -= 1
	models.SaveMaidDate(database.DB, &data)

	for _, conf := range configuration.GetByDateAwardType(Type) {

		switch conf.AwardType {
		case 1:
			// 标准获得侍女经验 = 侍女魅力 * (侍女魅力/100 + 1)
			var baseCharm = float64(maid.Charm) * (float64(maid.Charm)/100 + 1)
			resp.Exp = int32(baseCharm / 10000 * float64(conf.RandN()))

		case 2:
			// 侍女亲密度
			resp.Intimacy = conf.RandN()

		case 3:
			// 觉醒一定概率奖励道具
			var r = rand.Int32(0, 9999)
			if r < conf.Weight {

				var number = conf.RandN()

				resp.RawItem = &pb.RawItem{ItemID: conf.Value, Number: number}
				resp.Item = itemC.AddItem(roleID, conf.Value, number)
			}
		}
	}

	var session = database.DB.Begin()

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	maid.Exp += resp.Exp
	maid.Intimacy += resp.Intimacy

	models.Save(session, &maid)
	models.UpdateIntimacy(session, roleID, h.GetServerID(ctx))
	session.Commit()

	resp.MaidDate = buildPbMaidDate(data, vip)
	resp.Maid = &maid

	// quest
	go questC.Date(roleID, h.GetServerID(ctx), 1, resp.Intimacy)
	return nil
}

func (h *MaidServiceHandler) DateStep2(ctx context.Context, req *pb.DateStep2Request, resp *pb.APIMaidDateReply) (err error) {

	var roleID = h.GetRoleID(ctx)
	if req.Answer < 1 || req.Answer > 4 {
		return errors.ParamError
	}

	var vip = roleC.GetVIP(roleID)
	var data = models.GetMaidDate(database.DB, roleID, vip)
	if data.DateNum <= 0 {
		return errors.ParamError
	}

	var maid = models.GetMaid(database.DB, roleID, req.MaidID)

	configs := configuration.GetByDateAwardType(req.Answer)

	for _, conf := range configs {

		switch conf.AwardType {
		case 1:
			// 标准获得侍女经验 = 侍女魅力 * (侍女魅力/100 + 1)
			var baseCharm = float64(maid.Charm) * (float64(maid.Charm)/100 + 1)
			resp.Exp = int32(baseCharm / 10000 * float64(conf.RandN()))

		case 2:
			// 侍女亲密度
			resp.Intimacy = conf.RandN()
		}
	}

	data.DateNum -= 1

	maid.Exp += resp.Exp
	maid.Intimacy += resp.Intimacy

	var session = database.DB.Begin()

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	models.Save(session, &maid)
	models.SaveMaidDate(session, &data)
	models.UpdateIntimacy(session, roleID, h.GetServerID(ctx))
	session.Commit()

	resp.Maid = &maid
	resp.MaidDate = buildPbMaidDate(data, vip)
	resp.Luck = false
	resp.Success = true

	// activity
	activityC.AddPoints(roleID, h.GetServerID(ctx), activityC.Intimacy, int64(resp.Intimacy))
	return nil
}

func (h *MaidServiceHandler) Ranking(_ context.Context, req *wrappers.Int32Value, resp *service.RankingPointsList) error {

	var data []entity.Intimacy

	database.DB.Where("server_id = ? AND intimacy > 0", req.Value).Order("intimacy DESC, updated_at ASC").Limit(200).Find(&data)

	resp.Ranking = make([]*service.RankingPoints, 0, len(data))
	for _, v := range data {
		resp.Ranking = append(resp.Ranking, &service.RankingPoints{RoleID: v.RoleID, Points: int64(v.Intimacy)})
	}

	return nil
}

func (h *MaidServiceHandler) MyRanking(ctx context.Context, _ *empty.Empty, resp *pb.RankingNumber) error {

	var roleID, serverID = h.GetRoleID(ctx), h.GetServerID(ctx)
	var data = models.GetIntimacy(database.DB, roleID, serverID)

	var number int32
	database.DB.Model(&data).Where("server_id = ? AND intimacy > ?", serverID, data.Intimacy).Count(&number)

	resp.Points = int64(data.Intimacy)
	resp.Number = number + 1
	return nil
}

func (h *MaidServiceHandler) RestoreDrink(ctx context.Context, _ *empty.Empty, resp *pb.MaidDate) error {

	var roleID = h.GetRoleID(ctx)
	if err := itemC.AsyncDelItem(roleID, h.GetServerID(ctx), 101301, 1); err != nil {
		return err
	}

	var VIP = roleC.GetVIP(roleID)
	var data = models.GetMaidDate(database.DB, roleID, VIP)
	data.DrinkTime = cd.NewCD(models.DrinkCD, VIP.Drink).SetNum(VIP.Drink)

	models.SaveMaidDate(database.DB, &data)

	*resp = *buildPbMaidDate(data, VIP)
	return nil
}

func (h *MaidServiceHandler) RestoreDate(ctx context.Context, _ *empty.Empty, resp *pb.MaidDate) error {

	var roleID = h.GetRoleID(ctx)
	if err := itemC.AsyncDelItem(roleID, h.GetServerID(ctx), 101302, 1); err != nil {
		return err
	}

	var VIP = roleC.GetVIP(roleID)
	var data = models.GetMaidDate(database.DB, roleID, VIP)
	data.DateTime = cd.NewCD(models.DateCD, VIP.Date).SetNum(VIP.Date)

	models.SaveMaidDate(database.DB, &data)

	*resp = *buildPbMaidDate(data, VIP)
	return nil
}

func (h *MaidServiceHandler) Garden(ctx context.Context, _ *empty.Empty, resp *pb.GardenResp) error {

	var role = roleC.GetRole(h.GetRoleID(ctx))

	var maids = models.GetGardenMaids(role.RoleID)

	resp.Maids = make([]*pb.GardenMaid, len(maids))
	for k, v := range maids {
		resp.Maids[k] = buildPbGarden(v)
	}

	var data = models.GetGarden(role.RoleID)

	resp.SeatNum = data.Seat
	resp.KidNum2 = level.GetMaxKid(role.Level)
	resp.KidNum1, resp.CD = cd.NewCD(models.KidCD, resp.KidNum2).SetTime(data.Time).GetNum()

	return nil
}

func (h *MaidServiceHandler) Kid(ctx context.Context, req *wrappers.Int32Value, resp *pb.KidResp) (err error) {

	var role = roleC.GetRole(h.GetRoleID(ctx))

	var maid *entity.GardenMaid
	maid, resp.Success = models.GetByRoleIDMasterID(role.RoleID, req.Value)
	if !resp.Success {
		return nil
	}

	if maid.Num <= 0 {
		return errors.ParamError
	}

	var data = models.GetGarden(role.RoleID)

	var max = level.GetMaxKid(role.Level)
	var n, _ = cd.NewCD(models.KidCD, max).SetTime(data.Time).GetNum()
	if n <= 0 {
		return errors.ParamError
	}

	resp.Award = awardM.RandAward(role, configuration.GetByKidNum(n))

	var session = database.DB.Begin()

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	maid.Num -= 1
	if err = models.SaveGardenMaid(session, maid); err != nil {
		return err
	}

	data.Time = cd.NewCD(models.KidCD, max).SetTime(data.Time).SetNum(n - 1)
	if err = models.SaveGarden(session, data); err != nil {
		return err
	}

	session.Commit()

	resp.Maid = buildPbGarden(maid)
	return awardM.SendAward(role, resp.Award)
}

func (h *MaidServiceHandler) GetRansom(ctx context.Context, req *wrappers.Int32Value, resp *pb.GetRansomResp) error {

	var role = roleC.GetRole(h.GetRoleID(ctx))
	var data = models.GetLostMaids(role.RoleID)

	if v, ok := data[req.Value]; ok {
		resp.Gold = level.GetStandardGold(role.Level)
		resp.RoleID = v.RoleID
		resp.Name = role.Name
	}

	return nil
}

func (h *MaidServiceHandler) PayRansom(ctx context.Context, req *wrappers.Int32Value, _ *empty.Empty) error {

	var role = roleC.GetRole(h.GetRoleID(ctx))
	var data = models.GetLostMaids(role.RoleID)

	if v, ok := data[req.Value]; ok {

		var num = level.GetStandardGold(role.Level)
		if err := itemC.AsyncDelItem(h.GetRoleID(ctx), h.GetServerID(ctx), consts.Gold, num); err != nil {
			return err
		}

		var role = roleC.GetRole(h.GetRoleID(ctx))

		var content, _ = json.Marshal(map[string]interface{}{"ID": 110, "s1": role.Name, "s2": v.MaidID})
		_ = mailC.Send1(v.RoleID, "(h *MaidServiceHandler) PayRansom", string(content), pb.CreateAward(consts.Gold, num))

		return models.RemoveGardenMaid(v)
	}

	return nil
}

func (h *MaidServiceHandler) UpGarden(ctx context.Context, _ *empty.Empty, resp *wrappers.Int32Value) error {

	var data = models.GetGarden(h.GetRoleID(ctx))

	var diamond = configuration.GetByGardenNum(data.Seat)
	if err := itemC.AsyncDelItem(h.GetRoleID(ctx), h.GetServerID(ctx), consts.Diamond, diamond); err != nil {
		return err
	}

	data.Seat += 1

	resp.Value = data.Seat
	return models.SaveGarden(database.DB, data)
}

func (h *MaidServiceHandler) OneKeyKid(ctx context.Context, req *wrappers.Int32Value, resp *pb.KidResp) (err error) {

	var role = roleC.GetRole(h.GetRoleID(ctx))

	var maid *entity.GardenMaid
	maid, resp.Success = models.GetByRoleIDMasterID(role.RoleID, req.Value)
	if !resp.Success {
		return nil
	}

	if maid.Num <= 0 {
		return errors.ParamError
	}

	var data = models.GetGarden(role.RoleID)

	var max = level.GetMaxKid(role.Level)
	var n, _ = cd.NewCD(models.KidCD, max).SetTime(data.Time).GetNum()
	if n <= 0 {
		return errors.ParamError
	}

	var session = database.DB.Begin()

	defer func() {
		if r := recover(); r != nil {
			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	var sub = int32(math.Min(float64(n), float64(maid.Num)))

	maid.Num -= sub
	if err = models.SaveGardenMaid(session, maid); err != nil {
		return err
	}

	data.Time = cd.NewCD(models.KidCD, max).SetTime(data.Time).SetNum(n - sub)
	if err = models.SaveGarden(session, data); err != nil {
		return err
	}

	session.Commit()

	resp.Maid = buildPbGarden(maid)
	resp.Award = &pb.Award{}
	for i := int32(0); i < sub; i++ {

		var rawItem = awardM.RandAward(role, 4001).AwardItems[0].RawItem
		resp.Award.AddRawItem(rawItem)
	}

	return awardM.SendAward(role, resp.Award)
}

func (h *MaidServiceHandler) Capture(ctx context.Context, req *service.CaptureRequest, resp *wrappers.Int32Value) error {

	var roleID = h.GetRoleID(ctx)

	var gardenMaids = models.GetGardenMaids(roleID)

	if len(gardenMaids) >= int(models.GetGarden(roleID).Seat) {
		// 检查花园软禁位
		return nil
	}

	for _, v := range gardenMaids {
		if v.MasterID == req.MasterID {
			// 同一人只俘虏一个侍女
			return nil
		}
	}

	var maidIDs []int32
	var data = models.GetLostMaids(req.MasterID)

	var maids = models.FriendMaids(database.DB, req.MasterID)
	for _, maid := range maids {

		if _, ok := data[maid.MaidID]; !ok {
			maidIDs = append(maidIDs, maid.MaidID)
		}
	}

	if len(maidIDs) != 0 {

		resp.Value = maidIDs[rand.Int(0, len(maidIDs)-1)]

		return models.CreateGardenMaid(&entity.GardenMaid{
			RoleID:   roleID,
			MasterID: req.MasterID,
			MaidID:   resp.Value,
			RoleName: req.Name,
			Num:      10,
		})
	}

	return nil
}

func (h *MaidServiceHandler) SaveFromEnemy(ctx context.Context, req *wrappers.Int32Value, resp *wrappers.Int32Value) error {

	for _, v := range models.GetLostMaids(h.GetRoleID(ctx)) {

		if v.RoleID == req.Value {

			if err := models.RemoveGardenMaid(v); err != nil {
				return err
			}

			resp.Value = v.MaidID
		}
	}

	return nil
}

// 计算是否觉醒
func (h *MaidServiceHandler) isAwake(maid *pb.Maid) bool {

	const baseRate = 0.15

	// 查看觉醒比率
	var classConf, err = configuration.GetByMaidClass(maid.Class)
	if err != nil {
		panic(err)
	}

	var n = redis.GetTodayNumber(fmt.Sprintf("MaidAwake%d", maid.RoleID))
	if n < int(roleC.GetVIP(maid.RoleID).Benediction) && rand.IsWin(classConf.AwakeRate+baseRate) {

		redis.SetTodayNumber(fmt.Sprintf("MaidAwake%d", maid.RoleID), n+1)
		return true
	} else {
		return false
	}
}

func (h *MaidServiceHandler) exp(maid *pb.Maid, conf configuration.DateAwardConf) int32 {

	var baseCharm = float64(maid.Charm) * (float64(maid.Charm)/100 + 1)
	return int32(baseCharm / 10000 * float64(rand.Int32(conf.CountMin, conf.CountMax)))
}

func (h *MaidServiceHandler) 更新对应羁绊的船长(ctx context.Context, maidID int32) {

	var roleID = h.GetRoleID(ctx)

	var c = captain2.New(roleID)

	for _, v := range c.GetByReady() {

		var conf, _ = configuration2.GetByCaptainID(v.CaptainID)
		if conf.FetterMaid > 0 && fetter.GetFetter(conf.FetterMaid).MaidID == maidID {

			var mess = c.ToMessage(v.CaptainID)
			_ = broker.RolePublish(roleID, pb.WsCode_CodeCaptain, mess)

			if mess.BuildingID > 0 {
				go buildingC.ResetCoin(ctx, v.BuildingID, c.ToPbCaptains(nil))
			}
		}
	}
}

func buildPbGarden(data *entity.GardenMaid) *pb.GardenMaid {

	var res = pb.GardenMaid{
		MaidID:   data.MaidID,
		Name:     data.RoleName,
		Num:      data.Num,
		Sec:      data.GetSec(),
		MasterID: data.MasterID,
	}

	return &res
}

func buildPbMaidDate(data entity.MaidDate, vip *service.VIP) *pb.MaidDate {

	var n1, sec1 = cd.NewCD(models.DrinkCD, vip.Drink).SetTime(data.DrinkTime).GetNum()
	var n2, sec2 = cd.NewCD(models.DateCD, vip.Date).SetTime(data.DateTime).GetNum()

	var conf = configuration.GetByWishNum(data.BarWishNum)

	return &pb.MaidDate{
		RoleID:             data.RoleID,
		DateNum:            data.DateNum,
		Date:               data.Date,
		BarDrinkNum:        n1,
		BarDrinkRecoverSec: sec1,
		BarDateNum:         n2,
		BarDateRecoverSec:  sec2,
		BarLuck: &pb.BarLuck{
			Luck:     data.BarLuck,
			WishNum:  data.BarWishNum,
			NeedCoin: conf.CostCoin,
			RawItem:  &pb.RawItem{ItemID: consts.Gold, Number: conf.CostGold},
		},
	}

	//var today = date.Date(time.Now())
	//if today != e.Date {
	//
	//	maidDate.Date = today
	//	maidDate.DateNum = models.DateNum
	//	maidDate.BarLuck.Luck = models.BarLuck
	//	maidDate.BarLuck.WishNum = 0
	//}
}
